package com.gonsin.ta.logic.services;

import com.gonsin.ta.logic.controllers.po.TaSignalPost;
import com.gonsin.ta.logic.enums.TaResult;
import com.gonsin.ta.logic.TA;
import com.gonsin.ta.logic.event.TaInitBeforeFinishedEvent;
import com.gonsin.ta.logic.parse.Chain;
import com.gonsin.ta.logic.process.SignalCenter;
import com.gonsin.ta.logic.process.TaResponse;
import com.gonsin.ta.logic.exceptions.TaException;
import com.gonsin.ta.logic.parse.TaExecutorService;
import com.gonsin.ta.logic.config.BaseTaConfig;
import com.gonsin.ta.logic.parse.ITaParser;
import com.gonsin.ta.logic.parse.ParserFactory;
import com.gonsin.ta.logic.parse.condition.context.ChainContextImpl;
import com.gonsin.ta.logic.process.TaBus;
import com.gonsin.ta.logic.spring.spi.ContextCmpInit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 执行器实现类
 */
@Service
@Slf4j
public class TaExecutorImpl implements TaExecutorService, ApplicationListener<ApplicationStartedEvent> {

    private volatile boolean init = false;

    @Autowired private BaseTaConfig config;
    @Autowired private ParserFactory parserFactory;

    @Autowired private SignalCenter signalCenter;

    @Autowired private ContextCmpInit contextCmpInit;

    private ApplicationContext applicationContext;

    @Override
    public void refresh() {

        // 加载本地一边写好的节点
//        ContextCmpInitHolder.loadContextCmpInit().initCmp();
        contextCmpInit.initCmp();

        // 加载本地流程文件
        Set<String> taFiles = new HashSet<>();

        String path = config.getDefaultTaPath();
        if(!path.endsWith("/")){
            path += "/";
        }
        if(path.startsWith("/")){
            path = path.substring(1);
        }
        File[] listFile = new File(path).listFiles();
        if(listFile != null && listFile.length > 0){

            // 添加本地文件路径
//            taFiles.addAll(Arrays.stream(listFile).filter(f -> f.getName().endsWith(".ta.xml"))
//                    .map(File::getPath)
//                    .collect(Collectors.toSet()));
            taFiles.addAll(Arrays.stream(listFile).map(File::getName)
                    .filter(name -> name.endsWith(".ta.xml"))
                    .collect(Collectors.toSet()));
        }
        // 读取resources下的文件
        Resource[] resources;
        try {
            resources = new PathMatchingResourcePatternResolver().getResources(path + "*.ta.xml");
            for (int i = 0; i < resources.length; i++) {
                String filename = resources[i].getFilename();
                taFiles.add(filename);
            }
        } catch (IOException e) {
            if(TA.LOG){
                log.warn("读取resources/{}出错", path);
            }
        }

        // 获取脚本解析器
        ITaParser parser = parserFactory.getInstance();
        try {
            parser.parseNode(taFiles);
            parser.parse(taFiles);
        } catch (Exception e) {
            log.error(e.toString(), e);
            throw new TaException(e.toString(), e);
        }

        applicationContext.publishEvent(new TaInitBeforeFinishedEvent(applicationContext));

        TaBus.notifyInitFinished();
    }

    @Override
    public TaResponse requestStart(String chainId, Map<String, Serializable> values) {
        if(!isInit()){
            refresh();
        }

        Chain chain = TaBus.getChain(chainId);
        if(chain == null){
            throw new TaException("找不到到【{}】流程定义", chainId);
        }

        return doExecute(chain, values);
    }

//    @Override
//    public TaResponse requestStart(Chain chain, Map<String, Serializable> values) {
//
//        if(!isInit()){
//            refresh();
//        }
//
//        return doExecute(chain, values);
//    }

    @Override
    public TaResponse postSignal(String instanceId, String signal, Map<String, Serializable> values) {
        TaSignalPost post = new TaSignalPost();
        post.setSignal(signal);
        post.setInstanceId(instanceId);
        post.setValues(values);
        return signalCenter.post(post);
    }

    private TaResponse doExecute(Chain chain, Map<String, Serializable> values) {
        try {
            // 初始化初始上下文
            ChainContextImpl context = new ChainContextImpl(applicationContext);
            context.setInstanceId(RandomStringUtils.random(32, true, true));
            context.setChainId(chain.getChainId());
            context.setValues(values);
            chain.execute(context);

            TaResponse response = new TaResponse();
            response.setInstanceId(context.getInstanceId());
            response.setContext(context);
            response.setResult(context.getResult());
            return response;

        } catch (Exception e) {
            log.error(e.toString(), e);

            TaResponse response = new TaResponse();
            response.setResult(TaResult.FAIL);

            throw new RuntimeException(e);
//            throw e;
//            return response;
        }
    }

    private boolean isInit(){
        return TaBus.isInit();
    }

    @Override
    public void onApplicationEvent(ApplicationStartedEvent event) {
        this.applicationContext = event.getApplicationContext();
        // 初始化
        long now = System.currentTimeMillis();
        refresh();
        long delay = System.currentTimeMillis() - now;
        if(TA.LOG){
            TA.log().info("TA - 初始化完成，用时{}秒", new SimpleDateFormat("ss").format(delay));
        }
        TA.__INSTANCE = this;
    }
}
